package com.example.springbootdemo.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.springbootdemo.bean.FileProperties;
import com.example.springbootdemo.bean.dto.ChunkInfoDTO;
import com.example.springbootdemo.bean.dto.FileInfoDTO;
import com.example.springbootdemo.bean.entity.ChunkInfo;
import com.example.springbootdemo.bean.entity.FileInfo;
import com.example.springbootdemo.bean.response.ResponseInfo;
import com.example.springbootdemo.mapper.ChunkInfoMapper;
import com.example.springbootdemo.mapper.FileInfoMapper;
import com.example.springbootdemo.mapstruct.ChunkInfoConvert;
import com.example.springbootdemo.mapstruct.FileInfoConvert;
import com.example.springbootdemo.util.FastDfsUtils;
import com.example.springbootdemo.util.FileUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author hh
 * @date 2022/3/1
 */
@Api(tags = "文件服务")
@RequestMapping("file")
@RestController
public class FileController {

    private final Logger log = LoggerFactory.getLogger(FileController.class);

    private final FileProperties fileProperties;

    private final ChunkInfoMapper chunkInfoMapper;

    private final FileInfoMapper fileInfoMapper;

    private final FastDfsUtils fastDfsUtils;

    public FileController(FileProperties fileProperties, ChunkInfoMapper chunkInfoMapper, FileInfoMapper fileInfoMapper, FastDfsUtils fastDfsUtils) {
        this.fileProperties = fileProperties;
        this.chunkInfoMapper = chunkInfoMapper;
        this.fileInfoMapper = fileInfoMapper;
        this.fastDfsUtils = fastDfsUtils;
    }

    /**
     * FastDFS文件上传
     * @param file 文件
     * @return 地址
     */
    @ApiOperation(("FastDFS文件上传"))
    @PostMapping("fdfs")
    public String uploadImage(@RequestParam("file") MultipartFile file){
        try {
            return fastDfsUtils.uploadImageFile(file);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "失败";
    }


    /**
     * 上传文件块
     *
     * @param chunkInfoDTO chunkInfoDTO
     * @return String
     */
    @ApiOperation("上传文件块")
    @PostMapping("uploadChunk")
    public String uploadChunk(@RequestParam("file") MultipartFile file,
                              @Validated ChunkInfoDTO chunkInfoDTO) {
        try {
            byte[] bytes = file.getBytes();
            Path path = Paths.get(FileUtils.generatePath(fileProperties.getPath(), chunkInfoDTO));
            // 文件写入指定路径
            Files.write(path, bytes);

            chunkInfoMapper.insert(ChunkInfoConvert.CHUNK_INFO_CONVERT.ChunkInfoDTOToChunkInfo(chunkInfoDTO));
            return "ok";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "fail";
    }

    /**
     * 检查分块
     *
     * @param chunkInfoDTO chunkInfoDTO
     * @return String
     */
    @ApiOperation("检查分块")
    @GetMapping("checkChunk")
    public ResponseInfo checkChunk(ChunkInfoDTO chunkInfoDTO) {
        // 判断整个文件是否上传过了，如果是，则告诉前端跳过上传，实现秒传
        String file = fileProperties.getPath() + File.separator + chunkInfoDTO.getIdentifier() + File.separator + chunkInfoDTO.getFileName();
        if (FileUtils.fileExists(file)) {
            JSONObject result = new JSONObject();
            result.put("status", 1);
            result.put("msg", "完整文件已存在，直接跳过上传，实现秒传");
            return ResponseInfo.success(result);
        }
        // 如果完整文件不存在，则去数据库判断当前哪些文件块已经上传过了，把结果告诉前端，跳过这些文件块的上传，实现断点续传
        LambdaQueryWrapper<ChunkInfo> chunkInfoLambdaQueryWrapper = Wrappers.lambdaQuery(ChunkInfo.class)
                .select(ChunkInfo::getChunkNumber)
                .eq(ChunkInfo::getIdentifier, chunkInfoDTO.getIdentifier())
                .eq(ChunkInfo::getFileName, chunkInfoDTO.getFileName());
        List<ChunkInfo> chunkInfos = chunkInfoMapper.selectList(chunkInfoLambdaQueryWrapper);
        if (CollectionUtils.isEmpty(chunkInfos)) {
            return ResponseInfo.success();
        }
        List<Integer> chunkNumbers = chunkInfos.stream().map(ChunkInfo::getChunkNumber).collect(Collectors.toList());
        return ResponseInfo.success(chunkNumbers);
    }

    /**
     * 合并文件
     *
     * @param fileInfoDTO fileInfoDTO
     * @return String
     */
    @ApiOperation("合并文件")
    @PostMapping("mergeFile")
    public String mergeFile(FileInfoDTO fileInfoDTO) {
        FileInfo fileInfo = FileInfoConvert.FILE_INFO_CONVERT.fileInfoDTOToFileInfo(fileInfoDTO);
        // 进行文件合并操作
        String fileName = fileInfo.getFileName();
        String folder = fileProperties.getPath() + fileInfo.getIdentifier();
        String filePath = folder + File.separator + fileName;

        String mergeCode = FileUtils.merge(filePath, folder, fileName);
        if (FileUtils.MERGE_FAIL.equals(mergeCode)){
            return "文件合并失败";
        }

        // 如果已经存在，则判断是否同一个项目，同一个项目的不用新增记录，否则新增
        if (FileUtils.MERGE_FILE_EXIST.equals(mergeCode)){
            LambdaQueryWrapper<FileInfo> fileInfoLambdaQueryWrapper = Wrappers.lambdaQuery(FileInfo.class)
                    .eq(FileInfo::getFileName, fileInfo.getFileName())
                    .eq(FileInfo::getIdentifier, fileInfo.getIdentifier())
                    .eq(FileInfo::getRefProjectId, fileInfo.getRefProjectId());
            Long count = fileInfoMapper.selectCount(fileInfoLambdaQueryWrapper);
            if (Objects.nonNull(count) && count > 0){
                return "成功";
            }
        }

        fileInfo.setLocation(filePath);

        // 文件合并成功后保存记录到数据库
        int insert = fileInfoMapper.insert(fileInfo);

        return insert > 0 ? "成功" : "失败";
    }


    /**
     * 文件上传
     *
     * @param file file
     * @return String
     */
    @ApiOperation("文件简单上传")
    @PostMapping("upload")
    public String upload(@RequestParam("file") MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        log.info("获取到的上传文件路径是：{}", fileProperties.getPath());
        Assert.hasText(originalFilename, "文件名不能为空");

        assert originalFilename != null;
        String cleanPath = StringUtils.cleanPath(originalFilename);
        boolean uploadFile = FileUtils.uploadFile(file, fileProperties.getPath() + cleanPath);

        return uploadFile ? "成功" : "失败";
    }

}
